﻿using Actors;
using Actors.ActorComponents;
using Actors.Actions;
using UnityEngine;

namespace GameStates
{

    /// <summary>
    /// 状态输入，用于获取状态机的输入 (或者直接使用内存读写？)
    /// </summary>
    public struct StateInput
    {
        public Vector2 vector2;
        public bool boolValue;
        public float floatValue;
        public int intValue;
        
    }

    public abstract class CharacterState : State
    {
        protected Character m_Character;
        protected InputComponent m_Input;

        public bool IsGrounded { get; protected set; }
        private const float GroundCheckDistance = 0.05f;
        private const float GroundCheckDistanceInAir = 0.07f;
        private const int GroundCheckLayers = -1;
        
        public virtual void Initialize(Character character)
        {
            m_Character = character;
            m_Input = m_Character.RequireActorComponent<InputComponent>();
        }

        public override void Update()
        {
            GroundCheck();
        }

        protected void HandleMoveInput()
        {
            var movementInput = m_Input.GetMoveInput();
            if (movementInput == Vector2.zero)
            {
                return ;
            }

            var acMove = Action.GetAction<Act_Move>(m_Character);
            acMove.MovementInput = movementInput;
            m_Character.ExecuteAction(acMove);
        }
        
        protected void HandleJumpInput()
        {
            if (!m_Input.IsPressedJump())
            {
                return;
            }

            var acJump = Action.GetAction<Act_Jump>(m_Character);
            m_Character.ExecuteAction(acJump);
        }


        #region PhysicsCheck

        protected void GroundCheck()
        {
            float chosenGroundCheckDistance = IsGrounded ? GroundCheckDistance : GroundCheckDistanceInAir;
            IsGrounded = Physics.CapsuleCast(m_Character.GetCapsuleBottomHemisphere(),
                m_Character.GetCapsuleTopHemisphere(),
                m_Character.BodyInfo.radius, Vector3.down, out RaycastHit _, chosenGroundCheckDistance,
                GroundCheckLayers,
                QueryTriggerInteraction.Ignore);
            
            // IsGrounded = Physics.Raycast(m_Character.GetCapsuleBottomHemisphere(), Vector3.down, m_Character.BodyInfo.radius + chosenGroundCheckDistance, GroundCheckLayers);
            // Debug.Log(IsGrounded);
        }


        #endregion
    }

    public class OnGroundState : CharacterState
    {
        public override void OnEnter()
        {
            
        }

        public override void OnExit()
        {
            StatePool.Return(this);
        }

        public override void FixedUpdate()
        {
            if (!IsGrounded)
            {
                var fall = Action.GetAction<Act_Fall>();
                m_Character.ExecuteAction(fall);
            }
        }
    }

    public class Idle : OnGroundState
    {
        public override void OnEnter()
        {
            
        }

        public override void OnExit()
        {
            StatePool.Return(this);
        }

        public override void Update()
        {
            base.Update();
            HandleMoveInput();
            HandleJumpInput();
        }
    }
    
    public class Move : OnGroundState
    {
        public Vector2 movementInput;

        public override void OnEnter()
        {
            
        }

        public override void OnExit()
        {
            StatePool.Return(this);
        }

        public override void Update()
        {
            base.Update();
            UpdateMoveInput();
            HandleJumpInput();
        }

        public override void FixedUpdate()
        {
            m_Character.Move(movementInput * (Time.fixedDeltaTime * m_Character.Speed));
        }

        private void UpdateMoveInput()
        {
            var moveInput = m_Input.GetMoveInput();
            movementInput = moveInput;
            if (moveInput.sqrMagnitude < 0.0001f)
            {
                var idle = Action.GetAction<Act_Idle>(m_Character);
                m_Character.ExecuteAction(idle);
            }
        }
    }
    
    public class Jump : CharacterState
    {
        private float m_JumpForce;
        private Vector3 m_Velocity;
        
        public override void Initialize(Character character)
        {
            base.Initialize(character);
            m_JumpForce = 5f;
            m_Velocity = character.Velocity;
        }

        public override void OnEnter()
        {
            m_Velocity.y += m_JumpForce;
        }

        public override void OnExit()
        {
            StatePool.Return(this);
        }

        public override void Update()
        {
            m_Velocity.y += Physics.gravity.y * Time.deltaTime;
            if (m_Velocity.y < 0)
            {
                m_Character.ExecuteAction(Action.GetAction<Act_Fall>(m_Character));
            }
        }
        
        public override void FixedUpdate()
        {
            if (IsGrounded)
            {
                m_Character.ExecuteAction(Action.GetAction<Act_Idle>(m_Character));
                return;
            }
            m_Character.Move(m_Velocity * Time.fixedDeltaTime);
        }
    }
    
    public class Fall : CharacterState
    {
        private const int MaxFallSpeed = 10;
        private Vector3 m_Velocity;
        public override void Initialize(Character character)
        {
            base.Initialize(character);
            m_Velocity = character.Velocity;
        }

        public override void OnEnter()
        {
            
        }

        public override void OnExit()
        {
            StatePool.Return(this);
        }
        
        public override void FixedUpdate()
        {
            m_Velocity.y += Physics.gravity.y * Time.fixedDeltaTime;
            m_Velocity.y = Mathf.Clamp(m_Velocity.y, -MaxFallSpeed, 0 );
            if (IsGrounded)
            {
                m_Character.ExecuteAction(Action.GetAction<Act_Land>(m_Character));
                return;
            }
            m_Character.Move(m_Velocity * Time.fixedDeltaTime);
        }
    }

    public class Land : CharacterState
    {
        public override void OnEnter()
        {
            m_Character.ExecuteAction(Action.GetAction<Act_Idle>(m_Character));
        }

        public override void OnExit()
        {
            StatePool.Return(this);
        }
    }
}